package top.rainbowecho.common.standard.generate;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import top.rainbowecho.common.standard.database.PropertyConfiguration;
import top.rainbowecho.common.standard.database.manager.DatabaseManager;
import top.rainbowecho.common.standard.database.manager.impl.DatabaseManagerImpl;
import top.rainbowecho.common.standard.generate.item.GenerateMetadata;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 杨健
 * @since 2019/8/22 17:18
 */
@Slf4j
public class EnumGenerate {
    private static final String RESOURCES_DIRECTORY = "src/main/resources/";

    /**
     * file separator
     */
    private static final String SEPARATOR = "/";

    private PropertyConfiguration propertyConfiguration = new PropertyConfiguration();

    /**
     * 根视图名的前缀，用于构成文件名
     */
    private String rootEntityName = "View";

    /**
     * 先通过查询数据库中的所有表，再根据每张表各个字段信息，自动生成枚举类
     *
     * @param packageName 目的包名
     * @param databaseName 数据库名
     * @throws Exception 生成异常
     */
    public void generateTableFieldEnum(String packageName, String databaseName) throws Exception {
        DatabaseManager databaseManager = new DatabaseManagerImpl();
        List<String> tables = databaseManager.selectAllTables(databaseName);

        final String[] entity = {null};

        tables.forEach(table -> {
            try {
                List<String> fields = databaseManager.selectTableFields(table);
                String subTableName = removePrefix(table, propertyConfiguration.getPrefix());
                entity[0] = lineToHump(subTableName);

                Map<String, String> fieldMap = fields.stream().collect(Collectors.toMap(String::toUpperCase, Function.identity()));
                String templateFilename = "tableTemplate.ftl";

                // 使用数据库表生成模板去生成enum
                GenerateMetadata metadata = new GenerateMetadata().setEntity(entity[0]).setFieldMap(fieldMap).setTargetPackage(packageName).setTemplateFilename(templateFilename);
                commonGeneratorEnum(metadata);
                log.info(String.format("%s generate success", entity[0]));
            } catch (Exception e) {
                log.error("Table generate failed");
            }
        });
    }

    /**
     * 去除数据库表名前缀
     *
     * @param table 表名
     * @param prefix 表名前缀
     * @return 返回去除结果
     */
    private String removePrefix(String table, String prefix) {
        String subTableName = table;
        if (table.startsWith(prefix)) subTableName = table.substring(prefix.length());
        return subTableName;
    }

    /**
     * 自动根据数据表元数据信息生成枚举类
     *
     * @param metadata 元数据信息
     */
    private void commonGeneratorEnum(GenerateMetadata metadata) {
        String targetDirectory = String.format("src/main/java/%s/", metadata.getTargetPackage().replace(".", SEPARATOR));

        Map<String, Object> enumParams = new HashMap<>(4);
        enumParams.put("package", metadata.getTargetPackage());
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        enumParams.put("date", LocalDateTime.now().format(dateTimeFormatter));
        enumParams.put("entity", metadata.getEntity());
        enumParams.put("fieldMap", metadata.getFieldMap());

        String templateDirectory = RESOURCES_DIRECTORY + "enum";

        // 文件名后缀要和ftl模板文件中定义的一样
        String targetFilename = metadata.getEntity() + "FieldEnum.java";

        File targetDirectoryFile = new File(targetDirectory);
        boolean mkdirSuccess = true;
        if (!targetDirectoryFile.exists()) mkdirSuccess = targetDirectoryFile.mkdirs();

        if (mkdirSuccess) {
            File targetFile = new File(targetDirectory + targetFilename);
            try (Writer writer = new FileWriter(targetFile)) {
                Template template = getConfiguration(templateDirectory).getTemplate(metadata.getTemplateFilename(), "UTF-8");
                template.process(enumParams, writer);
            } catch (IOException | TemplateException e) {
                log.error(String.format("%s make failed", targetFile));
            }
        }
    }

    /**
     * 根据多个目录下的视图文件，自动生成视图文件的枚举类
     *
     * @param targetPackage 输出包名
     * @param viewDirectories 视图文件存放的目录集合
     */
    public void generatorViewEnum(String targetPackage, String... viewDirectories) {
        // 先遍历视图文件目录
        for (String viewDirectory : viewDirectories) {
            String entity = isRoot(viewDirectory) ? rootEntityName : lineToHump(viewDirectory);
            String resourcesDirectory = RESOURCES_DIRECTORY + "templates/%s/";
            String path = String.format(resourcesDirectory, viewDirectory);
            File directory = new File(path);
            if (directory.exists()) {
                // 获取某一个目录下的html文件
                File[] files = directory.listFiles((dir, filename) -> {
                    if (filename.lastIndexOf('.') > 0) {
                        int lastIndex = filename.lastIndexOf('.');
                        String str = filename.substring(lastIndex);
                        return ".html".equals(str) || ".htm".equals(str);
                    }
                    return false;
                });

                if (files != null) {
                    // 将视图文件的"-"符替换成"_"
                    Map<String, String> fieldMap = Arrays.stream(files).collect(Collectors.toMap(file -> file.getName()
                            .split("\\.")[0]
                            .toUpperCase().replace("-", "_"), file -> {
                        String filename = file.getName();
                        if (isRoot(viewDirectory)) return filename;
                        else return viewDirectory + SEPARATOR + filename;
                    }));
                    String templateFilename = "viewTemplate.ftl";
                    GenerateMetadata metadata = new GenerateMetadata().setEntity(entity).setFieldMap(fieldMap).setTargetPackage(targetPackage).setTemplateFilename(templateFilename);
                    commonGeneratorEnum(metadata);
                    log.info(String.format("%s generate success", entity));
                } else {
                    log.error("not exist any html file.");
                }
            }
        }
    }

    private boolean isRoot(String viewDirectory) {
        return "/".equals(viewDirectory);
    }

    /**
     * 获取freemarker配置
     *
     * @param templateDirectory 目录文件所在目录
     * @return 返回freemarker配置
     */
    private Configuration getConfiguration(String templateDirectory) throws IOException {

        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        configuration.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
        configuration.setDirectoryForTemplateLoading(new File(templateDirectory));

        return configuration;
    }

    /**
     * 下划线转驼峰
     *
     * @param str 转换的str
     * @return 返回转换结果
     */
    private String lineToHump(String str) {
        String[] strings = str.split("_");
        StringBuilder stringBuilder = new StringBuilder();
        for (String string : strings) {
            stringBuilder.append(string.substring(0, 1).toUpperCase()).append(string.substring(1));
        }
        return stringBuilder.toString();
    }
}
